﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;

namespace sinetd
{
    public class Configuration : IXmlSerializable
    {
        [XmlIgnore()]
        public Dictionary<string, IManagerElement> Services;

        [XmlIgnore()]
        public Dictionary<string, IManagerElement> Protocols;

        [XmlIgnore()]
        public Dictionary<string, string> Prot2Serv;

        public List<IManagerElement> Serv2Prot(sinetd.Services.IService serv)
        {
            List<IManagerElement> list = new List<IManagerElement>();
            foreach (KeyValuePair<string, string> kvp in Prot2Serv)
            {
                if (kvp.Value == serv.Id)
                {
                    list.Add(Protocols[kvp.Key]);
                }
            }
            return list;
        }

        public void Load(string fileName)
        {
            if (File.Exists(fileName))
            {
                using (XmlReader reader = new XmlTextReader(new StreamReader(fileName)))
                {
                    this.ReadXml(reader);
                }
            }
            else
            {
                Protocols = new Dictionary<string, IManagerElement>();
                Services = new Dictionary<string, IManagerElement>();
                Prot2Serv = new Dictionary<string, string>();
            }
        }

        public void Save(string fileName)
        {
            using (XmlWriter writer = new XmlTextWriter(new StreamWriter(fileName)))
            {
                writer.WriteStartElement("Configuration");
                this.WriteXml(writer);
                writer.WriteEndElement();
            }
        }

        public sinetd.Services.IService GetService(string id)
        {
            return (sinetd.Services.IService)Services[Prot2Serv[id]];
        }

        #region IXmlSerializable

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            Protocols = new Dictionary<string, IManagerElement>();
            Services = new Dictionary<string, IManagerElement>();
            Prot2Serv = new Dictionary<string, string>();

            bool wasEmpty = reader.IsEmptyElement;
            reader.Read();

            if (wasEmpty) return;

            Stack<string> s = new Stack<string>();
            Dictionary<string, string> dict = new Dictionary<string, string>();

            reader.MoveToElement();

            while (reader.Read())
            {
                reader.MoveToContent();

                if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Protocol" && s.Peek() == "Protocol")
                {
                    sinetd.Protocols.IProtocol p = sinetd.Protocols.Manager.GetOrCreate(dict["ProtocolName"], dict.ContainsKey("ProtocolId") ? dict["ProtocolId"] : "");
                    if (dict.ContainsKey("ProtocolId"))
                    {
                        ((BaseManagerElement)p).Id = dict["ProtocolId"];
                    }
                    dict["ProtocolId"] = p.Id;
                    if (dict.ContainsKey("ProtocolConfig"))
                        p.Config = dict["ProtocolConfig"];
                    this.Protocols.Add(p.Id, p);
                    if (!dict.ContainsKey("ServiceId")) dict["ServiceId"] = p.Id + "heh";
                    Prot2Serv.Add(p.Id, dict["ServiceId"]);

                    dict.Remove("ProtocolId");
                    dict.Remove("ProtocolName");
                    dict.Remove("ProtocolConfig");
                }
                else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Service" && s.Peek() == "Service")
                {
                    sinetd.Services.IService srv = sinetd.Services.Manager.GetOrCreate(dict["ServiceName"], dict.ContainsKey("ServiceId") ? dict["ServiceId"] : "");
                    Prot2Serv = Prot2Serv.ToDictionary(k => k.Key, k => k.Value == dict["ServiceId"] ? srv.Id : k.Value, StringComparer.Ordinal);
                    dict["ServiceId"] = srv.Id;
                    if (dict.ContainsKey("ServiceConfig"))
                        srv.Config = dict["ServiceConfig"];
                    this.Services.Add(srv.Id, srv);

                    dict.Remove("ServiceId");
                    dict.Remove("ServiceName");
                    dict.Remove("ServiceConfig");
                }

                if (reader.NodeType == XmlNodeType.Element)
                {
                    s.Push(reader.Name);
                }
                else if (reader.NodeType == XmlNodeType.EndElement)
                {
                    s.Pop();
                }
                else
                {
                    if (s.Count > 1)
                    {
                        dict[s.ElementAt(1) + s.Peek()] = reader.ReadString();
                        s.Pop();
                    }
                }
            }
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("Services");
            foreach (IManagerElement elem in this.Services.Values)
            {
                writer.WriteStartElement("Service");

                writer.WriteElementString("Id", elem.Id);
                writer.WriteElementString("Name", elem.Name);
                writer.WriteElementString("Config", elem.Config);

                writer.WriteStartElement("Protocols");
                foreach (string elems in this.Prot2Serv.Where(k => k.Value == elem.Id).ToDictionary(t => t.Key, t => t.Value, StringComparer.Ordinal).Keys)
                {
                    IManagerElement elem2 = this.Protocols[elems];
                    writer.WriteStartElement("Protocol");

                    writer.WriteElementString("Id", elem2.Id);
                    writer.WriteElementString("Name", elem2.Name);
                    writer.WriteElementString("Config", elem2.Config);

                    writer.WriteEndElement();
                }
                writer.WriteEndElement();

                writer.WriteEndElement();
            }
            writer.WriteEndElement();
        }

        #endregion
    }
}
